package com.zixi.ai.framework.dao;

import com.zixi.ai.common.exception.ServiceException;
import com.zixi.ai.common.utils.sql.SqlUtil;
import jakarta.persistence.*;
import org.hibernate.jpa.spi.NativeQueryListTransformer;
import org.hibernate.jpa.spi.NativeQueryTupleTransformer;
import org.hibernate.query.NativeQuery;
import org.springframework.data.domain.*;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.jpa.repository.support.SimpleJpaRepository;

import java.math.BigInteger;
import java.util.List;
import java.util.Map;

/**
 * @author mqiqe
 */
public class BaseRepositoryImpl<T, ID> extends SimpleJpaRepository<T, ID> implements BaseRepository<T, ID> {
    EntityManager entityManager;

    /**
     * 父类没有不带参数的构造方法，这里手动构造父类
     */
    public BaseRepositoryImpl(Class<T> domainClass, EntityManager entityManager) {
        super(domainClass, entityManager);
        this.entityManager = entityManager;
    }

    @Override
    public <M> Page<M> findPage(String sql, Class<M> cls, Pageable pageable) {
        String countSql = "select count(*) from ( " + SqlUtil.adaptConvertSql(sql) + " ) t";
        Query query = entityManager.createNativeQuery(SqlUtil.adaptConvertSql(countSql));
        Object tmp = query.getSingleResult();

        long total = 0L;
        if (tmp instanceof BigInteger bi) {
            total = bi.longValue();
        }
        // 获取结果集
        query = entityManager.createNativeQuery(SqlUtil.adaptConvertSql(sql), cls);
        query.unwrap(NativeQuery.class).setTupleTransformer(NativeQueryListTransformer.INSTANCE);
        query.setFirstResult((int) pageable.getOffset());
        query.setMaxResults(pageable.getPageSize());
        List rs = query.getResultList();
        return new PageImpl<>(rs, pageable, total);
    }

    @Override
    public <M> List<M> findAll(String sql, Class<M> cls) {
        Query query = entityManager.createNativeQuery(SqlUtil.adaptConvertSql(sql));
        query.unwrap(NativeQuery.class).setTupleTransformer(NativeQueryListTransformer.INSTANCE);
        return query.getResultList();
    }

    @Override
    public <M> List<M> findAll(String sql, Class<M> cls, Object... params) {
        Query query = entityManager.createNativeQuery(SqlUtil.adaptConvertSql(sql));
        if (params != null && params.length > 0) {
            for (int i = 0; i < params.length; i++) {
                query.setParameter(i + 1, params[i]);
            }
        }
        query.unwrap(NativeQuery.class).setTupleTransformer(NativeQueryListTransformer.INSTANCE);
        return query.getResultList();
    }

    @Override
    public List<Map<String, Object>> findAllMap(String sql, Object... params) {
        Query query = entityManager.createNativeQuery(SqlUtil.adaptConvertSql(sql));
        if (params != null && params.length > 0) {
            for (int i = 0; i < params.length; i++) {
                query.setParameter(i + 1, params[i]);
            }
        }
        query.unwrap(NativeQuery.class).setTupleTransformer(NativeQueryListTransformer.INSTANCE);
        return query.getResultList();
    }

    @Override
    public List<T> findAll(String hql, Object... params) {
        Query query = entityManager.createQuery(hql);
        if (params != null && params.length > 0) {
            for (int i = 0; i < params.length; i++) {
                query.setParameter(i + 1, params[i]);
            }
        }
        @SuppressWarnings("unchecked")
        List<T> datas = query.getResultList();
        return datas;
    }

    @Override
    public <M> List<M> findAllM(String hql, Object... params) {
        Query query = entityManager.createQuery(hql);
        if (params != null && params.length > 0) {
            for (int i = 0; i < params.length; i++) {
                query.setParameter(i + 1, params[i]);
            }
        }
        @SuppressWarnings("unchecked")
        List<M> datas = query.getResultList();
        return datas;
    }

    @Override
    public Page<T> findAll(Specification<T> spec, Pageable pageable, Sort sort) {
        Pageable page = PageRequest.of(pageable.getPageNumber(), pageable.getPageSize(), sort);
        return findAll(spec, page);
    }

    @Override
    public <M> M findOne(String sql, Class<M> cls) {
        try {
            return (M) entityManager.createNativeQuery(SqlUtil.adaptConvertSql(sql), cls)
                    .unwrap(NativeQuery.class)
                    .setTupleTransformer(NativeQueryTupleTransformer.INSTANCE)
                    .getSingleResult();
        } catch (NoResultException e) {
            return null;
        } catch (NonUniqueResultException e) {
            throw new IllegalStateException("查询返回了多个结果，预期是单个结果", e);
        } catch (PersistenceException e) {
            throw new RuntimeException("执行原生查询时发生错误", e);
        }
    }

    @Override
    public long excute(String sql) {
        try {
            return entityManager.createNativeQuery(SqlUtil.adaptConvertSql(sql)).executeUpdate();
        } catch (Exception e) {
            return 0L;
        }
    }

    @Override
    public Page<T> findPage(String hql, Pageable pageable) {
        return findPage(hql, pageable, new Object[]{});
    }

    @Override
    public Page<T> findPage(String hql, Pageable pageable, Object... params) {
        // 获取记录总数
        String countHql = getCountQuery(hql);
        Query query = entityManager.createQuery(countHql);
        if (params != null && params.length > 0) {
            for (int i = 0; i < params.length; i++) {
                query.setParameter(i + 1, params[i]);
            }
        }
        Long tmp = (Long) query.getSingleResult();
        long total = tmp == null ? 0 : tmp;
        // 获取结果集
        query = entityManager.createQuery(hql);
        if (params != null && params.length > 0) {
            for (int i = 0; i < params.length; i++) {
                query.setParameter(i + 1, params[i]);
            }
        }

        query.setFirstResult((int) pageable.getOffset());
        query.setMaxResults(pageable.getPageSize());
        @SuppressWarnings("unchecked")
        List<T> datas = query.getResultList();

        return new PageImpl<>(datas, pageable, total);
    }

    /**
     * 根据HQL语句，获得查询总记录数的HQL语句 如： select ... from Organization o where o.parent is
     * null select ... from User u order by u.id 经过转换，可以得到： select count(*) from
     */
    private String getCountQuery(String hql) {
        String returnHql = "";
        try {
            int index = hql.indexOf("from");
            if (index != -1) {
                returnHql = hql.substring(index);
            }
            index = returnHql.indexOf("order by");
            if (index != -1) {
                returnHql = returnHql.substring(0, index);
            }
            return "select count(*) " + returnHql;
        } catch (Exception e) {
            throw new ServiceException("无效的HQL查询语句！");
        }
    }
}
